package com.gitee.wsl.math.matrix

import kotlin.math.sqrt

/**
 * A matrix that can be inverted. Also, compute its determinant, norm, and
 * condition number.
 */
class InvertibleMatrix : LinearSystem {
    /**
     * Constructor.
     *
     * @param n
     * the number of rows = the number of columns
     */
    constructor(n: Int) : super(n) {}

    /**
     * Constructor.
     *
     * @param values
     * the array of values
     */
    constructor(values: Array<FloatArray>) : super(values) {}

    /**
     * Compute the inverse of this matrix.
     *
     * @return the inverse matrix
     * @throws MatrixException
     * if an error occurred
     */
    @Throws(MatrixException::class)
    fun inverse(): InvertibleMatrix {
        val inverse = InvertibleMatrix(m_nRows)
        val identity = IdentityMatrix(m_nRows)

        // Compute each column of the inverse matrix
        // using columns of the identity matrix.
        for (c in 0 until m_nCols) {
            val col: ColumnVector = solve(identity.getColumn(c), true)
            inverse.setColumn(col, c)
        }
        return inverse
    }

    /**
     * Compute the determinant.
     *
     * @return the determinant
     * @throws MatrixException
     * if an error occurred
     */
    @Throws(MatrixException::class)
    fun determinant(): Float {
        decompose()

        // Each row exchange during forward elimination flips the sign
        // of the determinant, so check for an odd number of exchanges.
        var determinant = (if (m_nExchangeCount and 1 == 0) 1 else -1).toFloat()

        // Form the product of the diagonal elements of matrix U.
        for (i in 0 until m_nRows) {
            val pi: Int = m_aPermutation!![i] // permuted index
            determinant *= m_aLU!!.at(pi, i)
        }
        return determinant
    }

    /**
     * Compute the Euclidean norm of this matrix.
     *
     * @return the norm
     */
    fun norm(): Float {
        var sum = 0f
        for (r in 0 until m_nRows) {
            for (c in 0 until m_nCols) {
                val v = m_aValues[r][c]
                sum += v * v
            }
        }
        return sqrt(sum)
    }

    /**
     * Compute the condition number based on the Euclidean norm.
     *
     * @return the condition number
     * @throws MatrixException
     * if an error occurred
     */
    @Throws(MatrixException::class)
    fun condition(): Float {
        return norm() * inverse().norm()
    }

    companion object{
        /** Multiplies a 1x3 row vector with a 3x3 matrix.  */
        fun matrixMultiply(row: DoubleArray, matrix: Array<DoubleArray>): DoubleArray {
            val a = row[0] * matrix[0][0] + row[1] * matrix[0][1] + row[2] * matrix[0][2]
            val b = row[0] * matrix[1][0] + row[1] * matrix[1][1] + row[2] * matrix[1][2]
            val c = row[0] * matrix[2][0] + row[1] * matrix[2][1] + row[2] * matrix[2][2]
            return doubleArrayOf(a, b, c)
        }
    }
}